#include "proc.h"
#include "stdio.h"
#include "typedef.h"
#include "asm.h"
#include "kernel.h"
#include "mm.h"
#include "interrupts.h"

// running counter of proc id's
static u32 proc_id_counter;
proc_t* cur_proc;

extern asm_proc_switch(proc_regs*, proc_regs*);


// sets up initial proc. we dont need to set the registers because they
// will be set first time this one is swapped out
void init_proc() {
  proc_id_counter = 1;
  cur_proc = (proc_t*)kmalloc(sizeof(proc_t));
  cur_proc->id = proc_id_counter++;
  cur_proc->quantum = INITIAL_QUANTUM;
  cur_proc->state = RUNNABLE;
  cur_proc->parent = cur_proc;
  cur_proc->page_dir = cur_dir;
  cur_proc->next = cur_proc;
  cur_proc->prev = cur_proc;
  // set scheduler to be timer callback
  // register_isr(TIMER_IRQ, schedule);
} 

u32 get_pid() {
  return cur_proc->id;
}

// makes a new process with duplicate address space as current one
u32 fork() {
  u32 cur_esp, i, j;
  pte_t* old_table;
  pte_t* new_table;
  cli();
  proc_t* new_proc = (proc_t*)kmalloc(sizeof(proc_t));
  new_proc->id = proc_id_counter++;
  new_proc->quantum = INITIAL_QUANTUM;
  new_proc->state = RUNNABLE;
  new_proc->parent = cur_proc;
  new_proc->page_dir = clone_pde(cur_proc->page_dir);
  
  // stack needs to be replicated
  for(i = PDE_OFFSET(STACK_START); i < PDE_OFFSET(0xF0000000); ++i) {
    if(cur_proc->page_dir->tables[i] != 0) {
      old_table = (pte_t*)(PG_FRAME((u32)cur_proc->page_dir->tables[i]) + KERNEL_VIRT_OFFSET);
      new_table = get_page();
      new_proc->page_dir->tables[i] = (pte_t*)(((u32)new_table - KERNEL_VIRT_OFFSET) | 0x3);

      for(j = 0; j < 1024; ++j) {
	if(old_table->pages[j] != 0) {
	  new_table->pages[j] = get_frame() | 0x3;
	  copy_frame(PG_FRAME((u32)old_table->pages[j]), PG_FRAME((u32)new_table->pages[j]));
	}
      }
    }
  }

  // insert new proc in list right after current one
  new_proc->next = cur_proc;
  new_proc->prev = cur_proc->prev;
  new_proc->prev->next = new_proc;
  cur_proc->prev = new_proc;
 
  sti();
  // return new proc is for parent, 0 for child
  if(cur_proc->id == new_proc->parent->id)
    return new_proc->id;
  else return 0;
}

void proc_switch(proc_t* proc) {
  proc_t* tmp = cur_proc;
  cur_proc = proc;
  switch_page_directory(cur_proc->page_dir);
  asm_proc_switch(&tmp->regs, &cur_proc->regs);
}

void schedule() {
  proc_switch(cur_proc->next);
 
  /*if(cur_proc->quantum <= 0) {
    cur_proc->quantum = INITIAL_QUANTUM;
    cur_proc = cur_proc->next;
    proc_switch(cur_proc->next);
    }*/
}


